
#ifndef _TTG_MATH_VEC4_
#define _TTG_MATH_VEC4_
#include "Common/Definitions.h"

#ifdef MICROSOFT_DIRECTX
#include <d3dx9math.h>
#endif

#include <math.h>
#include "Vector.h"

namespace ttg
{
	namespace math
	{
		template <class T> class vec3;
		template <class T> class vec4;

		template <class T>
		inline T dotProduct(const vec4<T> &a, const vec4<T> &b)
		{
			return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
		}

		template <class T>
		inline vec4<T> crossProduct(const vec4<T> &a, const vec4<T> &b)
		{
			return vec4<T>(a.y * b.z - a.z * b.y,
						   -(a.x * b.z - a.z * b.x),
						   a.x * b.y - a.y * b.x,
						   1.0f);
		}

		template <class T>
		class vec4 :public Vector
		{
			public:
				T x;
				T y;
				T z;
				T w;

				//Constructors

				inline vec4<T>()
				{
					this->x = 0.0f;
					this->y = 0.0f;
					this->z = 0.0f;
					this->w = 1.0f;
				}

				inline vec4<T>(const vec4<T> &vec)
				{
					this->x = vec.x;
					this->y = vec.y;
					this->z = vec.z;
					this->w = vec.w;
				}

				inline vec4<T>(const vec3<T> &vec)
				{
					this->x = vec.x;
					this->y = vec.y;
					this->z = vec.z;
					this->w = 1.0f;
				}

		#ifdef MICROSOFT_DIRECTX

				inline vec4<T>(const D3DXVECTOR3 &vec)
				{
					this->x = vec.x;
					this->y = vec.y;
					this->z = vec.z;
					this->w = 1.0f;
				}

				inline vec4<T>(const D3DXVECTOR4 &vec)
				{
					this->x = vec.x;
					this->y = vec.y;
					this->z = vec.z;
					this->w = vec.w;
				}

		#endif
			    
				inline vec4<T>(T x, T y, T z, T w)
				{
					this->x = x;
					this->y = y;
					this->z = z;
					this->w = w;
				}

				//Operators
			    
				inline vec4<T> operator -() const
				{
					return vec4<T>(-this->x,
								   -this->y,
								   -this->z,
								   -this->w);
				}
			    
				inline vec4<T> operator -(const vec4<T> &vec) const
				{
					return vec4<T>(this->x - vec.x,
								   this->y - vec.y,
								   this->z - vec.z,
								   this->w - vec.w);
				}
			    
				inline vec4<T> operator +(const vec4<T> &vec) const
				{
					return vec4<T>(this->x + vec.x,
								   this->y + vec.y,
								   this->z + vec.z,
								   this->w + vec.w);
				}
			    
				inline vec4<T> operator *(float a) const
				{
					return vec4<T>(this->x * a,
								   this->y * a,
								   this->z * a,
								   this->w * a);
				}
			    
				inline vec4<T> operator *(const vec4<T> &vec) const
				{
					return vec4<T>(this->x * vec.x,
								   this->y * vec.y,
								   this->z * vec.z,
								   this->w * vec.w);
				}
			    
				inline vec4<T> operator /(float a) const
				{
					return vec4<T>(this->x / a,
								   this->y / a,
								   this->z / a,
								   this->z / a);
				}
			    
				inline vec4<T> operator /(const vec4<T> &vec) const
				{
					return vec4<T>(this->x / vec.x,
								   this->y / vec.y,
								   this->z / vec.z,
								   this->w / vec.z);
				}
			    
				inline const vec4<T> &operator =(const vec4<T> &vec)
				{
					this->x = vec.x;
					this->y = vec.y;
					this->z = vec.z;
					this->w = vec.w;
					return *this;
				}
			    
				inline const vec4<T> operator +=(const vec4<T> &vec)
				{
					this->x += vec.x;
					this->y += vec.y;
					this->z += vec.z;
					this->w += vec.w;
					return *this;
				}
			    
				inline const vec4<T> operator -=(const vec4<T> &vec)
				{
					this->x -= vec.x;
					this->y -= vec.y;
					this->z -= vec.z;
					this->w -= vec.w;
					return *this;
				}
			    
				inline bool operator ==(const vec4<T> &vec) const
				{
					return isZero(fabs(this->x - vec.x) + 
								  fabs(this->y - vec.y) + 
								  fabs(this->z - vec.z) + 
								  fabs(this->w - vec.w));
				}

				inline bool operator !=(const vec4<T> &vec) const
				{
					return !isZero(fabs(this->x - vec.x) + 
								   fabs(this->y - vec.y) + 
								   fabs(this->z - vec.z) +
								   fabs(this->w - vec.w));
				}
			    
				inline T &operator[](const int n)
				{
					return (&x)[n];
				}
			    
				inline const T &operator[](const int n) const
				{
					return (&x)[n];
				}

				inline float getLength() const
				{
					return sqrt(x * x + y * y + z * z + w * w);
				}

				inline vec4<T> normalize() const
				{
					float len = this->getLength();
		#ifdef TTG_DEBUG
					if (isZero(len))
					{
						   return vec4<T>(0.0f, 0.0f, 0.0f, 0.0f);
					}
					else
		#endif
						return vec4<T>(x / len, y / len, z / len, w / len);
				}

				inline vec4<T> invert() const
				{
		#ifdef TTG_DEBUG
					if (isZero(this->x) || isZero(this->y) || isZero(this->y) ||isZero(this->w))
						return vec4<T>(0.0f, 0.0f, 0.0f, 0.0f);
		#endif
					return vec4<T>(1.0f / this->x, 1.0f / this->y, 1.0f / this->z, 1.0f / this->w);
				}

				inline T project(const vec4<T> &ort) const
				{
					return dotProduct(*this, ort);
				}

				inline operator vec3<T>() const
				{
					return vec3<T>(x, y, z);
				}

		#ifdef MICROSOFT_DIRECTX

				inline operator D3DXVECTOR3() const
				{
					return D3DXVECTOR3(x, y, z);
				}

				inline operator D3DXVECTOR4() const
				{
					return D3DXVECTOR4(x, y, z, w);
				}

		#endif

				virtual int getDimensions()
				{
					return 4;
				}

				virtual int getComponentSize()
				{
					return sizeof(T);
				}
				virtual std::string toString()
				{
					char buf[128];
					_snprintf_s(buf, 127, "(%f, %f, %f, %f)", x, y, z, w);
					return std::string(buf);
				}
		};
	}
}

#endif
